III.1.OBJECTIFS ET PLAN DU CHAPITRE:
Ce chapitre est consacré à l'étude du fonctionnement de la classe javascript XMLHttpRequest, et à celle de son utilisation
dans le contexte d'un site web. L'utilisation de cette classe est, en effet, la seule réelle spécificité de la programmation
AJAX.
Après quelques généralités sur le fonctionnement de la classe XMLHttpRequest et sur les modalités de son utilisation,
le chapitre étudie en détail les différentes ressources offertes par cette classe (attributs et méthodes). Le chapitre ne
nécessite qu'une connaissance basique de la programmation "objet" (instanciation d'un objet à partir d'une classe, notions
d'attributs et de méthodes). Les personnes qui désirent se familiariser avec ces notions peuvent consulter
la documentation en ligne:
III.2.GENERALITES SUR LA CLASSE XMLHttpRequest:
III.2.1.CONTEXTE D'UTILISATION:
Les objets de la classe XMLHttpRequest ne peuvent être créés et utilisés que dans des scripts javascript. Ceux-ci s'exécutent dans
le contexte d'un navigateur installé sur un client web. Il est donc nécessaire que l'exécution des scripts javascript soit autorisée
par l'utilisateur de ce navigateur.
III.2.2.CARACTERISTIQUE PRINCIPALE:
La particularité fonctionnelle principale des objets de la classe est de permettre au navigateur de récupérer des données en
provenance du serveur sans utiliser le mécanisme de rechargement de page associés à la validation des liens hypertextes
ou des boutons "submit" des formulaires HTML: l'objet permet d'émettre vers le serveur une requête HTTP et de récupérer la
réponse à cette requête sans que le navigateur déclenche le mécanisme classique de changement de la page web en cours. Le
contenu renvoyé par le serveur est simplement stocké dans un attribut de l'objet et mis à disposition du script appelant. Le
développeur peut alors, dans la suite de l'exécution de ce script, faire de ce contenu l'utilisation qui lui convient, dans
les limites des possibilités offertes par javascript.
III.2.3.CREATION D'UN OBJET DE LA CLASSE XMLHttpRequest:
Pour pouvoir utiliser des objets de la classe XMLHttpRequest, il faut dans un premier temps instancier ces objets à partir
de cette classe. Cette instanciation est obtenue par le classique opérateur "new". Cependant, suivant le navigateur utilisé,
il peut exister une différence dans la programmation. En effet:
- Avec tous les navigateurs autres qu'Internet Explorer, la classe XMLHttpRequest fait partie d'une bibliothèque de base du
langage javascript. L'instanciation d'un objet respecte donc la syntaxe classique suivante:
Création de l'objet XHR : var XHR = new XMLHttpRequest();
- En revanche, sous Internet Explorer, la classe fait partie du plugin ActiveX. La création de l'objet se fait donc
de la manière suivante:
Création de l'objet XHR : XHR = new ActiveXObject("Microsoft.XMLHTTP");
La disponibilité de la bibliothèque AJAX ne coule évidemment pas de source pour tous les navigateurs. Il importe donc, avant
toute utilisation de celle-ci, de s'assurer de son existence. Il suffira pour cela de tester dans le script d'appel la présence
de l'objet du D.O.M. (Document Object Model) window.ActiveXObject pour I.E. ou window.XMLHttpRequest pour les autres
navigateurs. Ces tests permettront également de déterminer le type de syntaxe à employer pour l'instanciation de l'objet.
REMARQUE:
Notons que dans ces deux expressions, la chaîne "window" n'a aucun rapport avec le système d'exploitation WINDOW de Microsoft:
elle désigne l'objet "window" du D.O.M.. Les exemples de programmation donnés ici sont donc valables pour LINUX ou
UNIX.
La mise en oeuvre de la classe XMLHttpRequest exige donc une séquence d'instructions javascript semblable à celle-ci:
if (window.XMLHttpRequest)
{
XHR = new XMLHttpRequest();
}
else if (window.ActiveXObject)
{
XHR = new ActiveXObject("Microsoft.XMLHTTP");
}
else
{
window.alert("Votre navigateur n'est pas apte à utiliser les objets XMLHttpRequest.");
}
.....
.....
III.3.UTILISATION DE LA CLASSE XMLHttpRequest:
Cette section ne détaille que l'utilisation des attributs et méthodes les plus utilisés. Pour une description complète
des propriétés de la classe, voir le sous-chapitre III.5.
III.3.1.OUVERTURE D'UN ECHANGE (METHODE OPEN):
Avant tout échange AJAX, il est nécessaire d'ouvrir l'échange HTTP. En effet, ce protocole est en mode "connecté".
Il exige donc, en préalable à tout transfer de données, de définir les caractéristiques de cet échange, et en particulier:
- Le type de requête HTTP qui va être utilisée pour l'interrogation du serveur (GET, POST ou HEAD).
- L'URL du fichier que le serveur doit expédier au client
- Le mode de déroulement de l'échange (synchrone ou asynchrone).
C'est la méthode
OPEN qui permet d'ouvrir un échange. La syntaxe d'appel est la suivante:
XHR.open(<"GET"|"POST">, <URL du document>, <true/false>, [,user, password] );
REMARQUES:
- L'URL doit pointer sur un document dont le type est compatible avec celui d'une "page web" (HTML, PHP, XML, etc.)
- Pour fonctionner en mode asynchrone, le troisième argument devra être la valeur true
- Pour fonctionner en mode synchrone, le troisième argument devra être la valeur false
- User et password peuvent être omis si l'accès au fichier serveur n'est pas sécurisé par mot de passe.
EXEMPLES:
XHR.open("GET", "http://www.MonSite.com/MaPage.php" ,false );
XHR.open("POST", "http://www.SonSite.com/SaPage.php", true, "MonId", "MonPasswd" );
III.3.2.PARTICULARISATION DES EN-TETES D'ECHANGE (METHODE setRequestHeader):
La méthode setRequestHeader permet de particulariser les headers d'échanges des requêtes HTTP GET ou POST déclenchée en fixant
la valeurs de certains de leurs champs. La syntaxe est la suivante :
XHR.setRequestHeader( <Nom du champ>, <Valeur du champ> );
EXEMPLE:
XHR.setRequestHeader( "Content-type", "application/x-www-form-urlencoded" );
III.3.3.ENVOI D'UNE REQUETE DE TELECHARGEMENT AU SERVEUR (METHODE SEND):
Une fois l'échange initialisé, l'envoi de la requête au serveur est déclenché par la méthode SEND, dont voici la syntaxe:
XHR.send( <Liste des arguments de la requête> );
L'argument "liste des arguments" est une chaîne de caractères formatée suivant les mêmes règles qu'une liste d'arguments associés
à une URL: <nom_arg1=valeur_arg1> & <nom_arg2=valeur_arg2> &...& <nom_argN=valeur_argN>.
Cette liste d'arguments sera intégrée à la requête GET ou POST expédiée vers le serveur.
Le choix du mode de fonctionnement (synchrone ou asynchrone), défini par la méthode OPEN, influe sur le comportement de la méthode
SEND:
- En mode SYNCHRONE, la méthode SEND bloque l'exécution du script après l'émission de la requête, jusqu'à la
réception de la réponse du serveur. De ce fait, l'affichage de la page web est également bloqué pendant cette durée et
l'utilisateur "perd la main" jusqu'à l'arrivée de la réponse.
- En mode ASYNCHRONE, l'exécution du script reprend dès que la requête est émise. L'affichage de la page web et
la prise en compte des interactions avec l'utilisateur continuent donc pendant l'échange client-serveur: l'utilisateur "garde
la main".
EXEMPLE :
XHR.open("GET", "http://www.MonSite.com/MaPage.php", false );
XHR.send ( "marque=renault&type=megane" );
"http://www.MonSite.com/MaPage.php?marque=renault&type=megane"
III.3.4.RECEPTION EN MODE SYNCHRONE (ATTRIBUTS ResponseText et ResponseXML):
Lorsque la valeur du troisième argument de la méthode OPEN est "false", l'échange se déroule en mode SYNCHRONE. Nous avons vu que
dans ce mode de fonctionnement, l'exécution du programme javascript est bloquée jusqu'à la réception du fichier pointé par l'URL
figurant en deuxième argument. A la réception de ces données, elles sont stockées dans des attributs de l'objet XHR:
- Si le fichier appelé est un fichier XML, son contenu est stocké dans l'attribut responseXML
- Si le fichier appelé est un fichier HTML, son contenu est stocké dans l'attribut responseText.
L'exécution du script reprend alors et le développeur peut récupérer les données transmises (dans XHR.ResponseXML, si un fichier
XML a été transmis ou bien dans XHR.ResponseText, si c'est un fichier HTML qui a été transmis).
REMARQUES:
- Le fichier reçu est un fichier HTML dans le cas où la cible de l'URL est un fichier HTML, mais aussi dans le cas où
celle-ci est un fichier de script que le traitement du serveur va convertir en HTML (ex: fichier PHP).
- Dans tous les cas, le fichier reçu sera un fichier texte (HTML ou XML).
- Si le contenu reçu est un fichier XML, les arguments et contenus des différents noeuds définis dans XHR.ResponseXML
pourront être accédés directement par les fonctions du D.O.M.
EXEMPLES:
XHR.open("GET","http://www.MonSite.com/MaPage.html", false );
XHR.send( "" );
// Ici, l'exécution est mise en arrêt jusqu'à réception des données...
window.alert(XHR.responseText);
XHR.open("GET","http://www.MonSite.com/MaPage.xml", false );
XHR.send( "" );
window.alert(XHR.responseXML);
III.3.5.RECEPTION EN MODE ASYNCHRONE (ATTRIBUT onreadystatechange):
Nous avons vu qu'en mode asynchrone l'exécution du script n'était pas interrompue par la méthode SEND. Il est donc indispensable
de disposer d'un mécanisme permettant au développeur de détecter l'arrivée de la réponse du serveur. Ce mécanisme est un
gestionnaire d'événement particulier, associé à l'attribut onreadystatechange.
Tout gestionnaire d'événement peut être décrit comme un mécanisme logiciel, en général associé à un pilote de périphérique, et
chargé de surveiller l'arrivée d'un événement interne ou externe à la machine, puis de "prévenir" des processus logiciels en cours
d'exécution de cette arrivée. Lorsqu'un processus logiciel doit être prévenu de la survenue d'un événement, il suffit que le
développeur fournisse au gestionnaire concerné un "point d'entrée" dans ce processus. Ce point d'entrée est, la plupart du temps,
l'adresse d'un sous-programme de ce processus (une fonction, une méthode...) chargé de traiter l'événement.
Dans le cas qui nous occupe, la classe XMLHttpRequest se charge de traiter le paramétrage du gestionnaire. Le développeur
se contente donc de fournir à son objet XMLHttpRequest, dans l'attribut onreadystatechange, l'adresse de ce
"point d'entrée": il s'agit en fait du nom de la fonction que le développeur désire voir exécuter lors de l'arrivée de
la réponse du serveur.
Le schema ci-dessous matérialise le traitement de la réception des données dans le cas d'un échange asynchrone:
COMMENTAIRES:
Sur ce schema, nous voyons apparaître:
- A gauche, les couches matérielles de la machine cliente. Celles-ci sont capables de détecter les signaux en provenance
de la périphérie (en particulier les messages et requêtes en provenance du réseau) et de les transformer en événements
gérables par le logiciel.
- Au centre, un gestionnaire d'événement. Il s'agit d'un logiciel qui peut être "réveillé" (lancé) par le matériel
lorsqu'un événement survient.
- Le gestionnaire d'événement a accès à une liste de "points d'entrée" à activer lorsque l'événement survient. Cette
liste est également accessible aux processus utilisateurs, qui peuvent y charger des adresses de points d'entrée.
- Lorsque le développeur d'un processus utilisateur veut que celui-ci soit informé de la survenue d'un événement, il
lui suffit d'incorporer dans le processus un segment de programme qui charge la liste des points d'entrées avec l'adresse
d'une de ses fonctions internes. Ici, par exemple, le Processus 1 charge dans la liste l'adresse de la fonction
f1, le Processus 2 charge dans la liste l'adresse de la fonction f2. Dans le cas d'ajax, il suffit que le développeur
charge le nom de la fonction à activer dans l'attribut onreadystatechange
- Lorsque survient l'événement (par exemple, la réception de la réponse du serveur), le matériel "réveille" le
gestionnaire d'événement. Celui-ci active alors tous les points d'entrée figurant dans sa liste (ici, f1, f2, f3...).
Ces fonctions sont alors exécutées et accomplissent, pour leurs processus respectifs, les traitements prévus par le
développeur.
Le gestionnaire d'événement lié à XMLHttpRequest ne se contente pas de gérer uniquement l'évenement de réception de la réponse
du serveur. En fait, il nous renseigne sur toutes les phases de la transaction, au moyen d'un autre élément de la classe
XMLHttpRequest, l'attribut readyState: à chaque phase de la transaction, le "point d'entrée" est appelé avec une valeur de
readyState différente:
- SI readyState = 1: PHASE 1: l'échange n'est pas initialisé
- SI readyState = 2: PHASE 2: Les données sont en cours de réception
- SI readyState = 3: PHASE 3: l'échange est suffisamment avancé pour que l'utilisateur puisse accéder aux données
- SI readyState = 4: PHASE 4: les données sont entièrement disponibles.
Il sera donc nécessaire, dans la fonction correspondant au point d'entrée, de tester la valeur de readyState avant de lancer
le traitement des données reçues (il faudra que readyState soit au moins égal à 3). Comme dans le cas d'un échange synchrone,
ces données seront trouvées dans les attributs responseXML ou ResponseText.
III.4.EXEMPLE DE PROGRAMMATION AJAX:
III.4.1.PRINCIPE:
L'exemple présenté ci-dessous consiste à afficher dans un cadre le contenu d'un fichier HTML situé sur le serveur (une simple
liste). L'affichage est obtenu par un "clic" sur ce cadre. Le traitement des données reçues a volontairement été réduit au minimum
(un simple remplissage du cadre) de façon à concentrer l'attention sur la programmation du mécanisme AJAX. Les données à afficher
sont contenues dans le fichier du serveur "./Rep_Chapitre_2/Contenu_Exemple_3_1.html". Il s'agit d'une simple liste:
<!-- Fichier Contenu_Exemple_3_1.html -->
<ul>
<li>Janvier</li>
<li>Février</li>
<li>Mars</li>
<li>Avril</li>
<li>Mai</li>
<li>Juin</li>
<li>Juillet</li>
<li>Août</li>
<li>Septembre</li>
<li>Octobre</li>
<li>Novembre</li>
<li>Décembre</li>
</ul>
III.4.2.ORGANISATION GENERALE:
Outre la page principale en html qui affichera le cadre, le logiciel va se présenter sous la forme d'un fichier javascript
(JS_Exemple_3_1.js) décrivant une classe GestionAjax munie d'un constructeur ( GestionAjax() ), auquel reviendra la tâche de
créer l'objet XMLHttpRequest, et de 2 méthodes:
- Une méthode "Envoi", qui sera appelée par l'événement "clic souris" sur le cadre et qui permettra d'émettre vers le
serveur une requête AJAX GET en mode asynchrone.
- Une méthode "Reception", qui sera appelée par le gestionnaire de l'événement réception et qui permettra d'afficher
le contenu reçu dans le cadre.
Nous aurons donc à créer 3 fichiers: index.html, Contenu.html et JS.js, que nous placerons dans le sous-répertoire "Exemple"
de la racine de notre serveur.
III.4.3.PAGE PRINCIPALE (fichier index.html):
Voici le contenu de cette page. On remarquera le petit script ajax directement intégré dans le code html: il permet
de créer un objet GestionAjax. Au cours de cette création, un objet de type XMLHttpRequest est crée. Le lancement de ce code
a été placé ici car il suffit de créer l'objet une fois (et non à chaque émission de requête). On remarquera également que
l'événement onClick a été ajouté à la balise créant le cadre à remplir et que l'événement active la méthode "Envoi" de l'objet
GestionAjax.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" >
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Exemple de programmation AJAX</title>
<script type="text/javascript" src="JS_Exemple_3_1.js"></script>
<script>
// Création de l'objet GestionAjax: le constructeur crée lui-même un objet XMLHttpRequest
var ObjGestionAjax = new GestionAjax ( Envoi, Reception ); // Création d'un objet GestionAjax
</script>
</head>
<body>
<p style="margin: auto; text-align: center">Pour afficher, cliquer dans le cadre ci-dessous:</p>
<div id="Affichage" style="margin: auto; text-align: center; width: 200px; min-height: 100px; border: 1px solid black;"
onclick="ObjGestionAjax.Envoi();">
</div>
</body>
</html>
III.4.4.CONSTRUCTEUR DE LA CLASSE GestionAjax (fichier JS.js):
Le rôle de ce constructeur est:
- De définir l'attribut XHR de la classe
- De définir les deux méthodes de la classe (Envoi et Reception)
- Puis, d'instancier un objet de la classe XMLHttpRequest et de placer une référence à cet objet dans l'attribut XHR.
Nous placerons le contenu suivant dans le fichier JS.js:
//----------------------------------------------------------------------------------------------
// Fichier: JS_Exemple_3_1_GestionAjax.js
// Constructeur de la classe GestionAjax.
// Le constructeur tente, en fonction du type de navigateur, de créer un objet XMLHttpRequest.
// Si la création est réussie, l'objet est pointé par l'attribut XHR et la fonction retourne 0
// Sinon, XHR contient null
//--------------------------------------------------------------------------------------------
function GestionAjax ( Envoi, Reception )
{
// DECLARATION D'ATTRIBUTS
this.XHR = null;
// DECLARATION DE METHODES
this.Envoi = Envoi;
this.Reception = Reception;
// CREATION D'UN OBJET XMLHttpRequest
// Tenter de créer l'objet XHR en fonction du navigateur utilisé
if (window.XMLHttpRequest) // Firefox
{
this.XHR = new XMLHttpRequest();
}
else if (window.ActiveXObject) // Internet Explorer
{
this.XHR = new ActiveXObject("Microsoft.XMLHTTP");
}
else
{
// Signaler que XMLHttpRequest n'est pas supporté par le navigateur
window.alert("Votre navigateur n'est équipé pour utiliser les objets XMLHttpRequest.");
}
}
III.4.5.METHODE Envoi (fichier JS_Exemple_3_1.js):
Cette méthode permet de declencher l'envoi d'une requête HTTP de type GET vers le serveur, en mode asynchrone.
Nous placerons le contenu suivant dans le fichier JS_Exemple_3_1.js, à la suite du contenu existant:
//--------------------------------
// Fichier: JS_Exemple_3_1.js (Suite)
// Methode Envoi
//--------------------------------
function Envoi ()
{
// Ouverture de l'échange, requête GET, URL=Contenu.html, mode asynchrone.
this.XHR.open("get", "Dep_Chapitre_3/Contenu_Exemple_3_1.html", true);
// Choisir l'encodage correspondant au traitement choisi
this.XHR.setRequestHeader( "Content-type", "application/x-www-form-urlencoded" );
// Connexion l'événement de réception à la fonction de traitement
this.XHR.onreadystatechange = Rec; // Connexion du gestionnaire d'événement de réception à une fonction exterieure à
// la classe. Cette fonction appellera la méthode Reception (l'appel direct de la
// methode ne fonctionne pas!!!)
// Envoi de la requète
this.XHR.send( "" ); // envoi de la requête avec une liste d'arguments vide
}
III.4.6.METHODE Reception (fichier JS_Exemple_3_1.js):
Cette méthode permet de traiter l'événement de réception de la réponse du serveur à la requête AJAX. Il faut noter
qu'appeler directement cette methode par le gestionnaire d'événement (en programmant dans la fonction "Envoi"
l'instruction "this.XHR.onreadystatechange = this.Reception;" semble ne pas fonctionner. Pour pallier cet inconvénient, on
utilise ici une fonction intermédiaire non déclarée comme une méthode: la fonction "Rec", qui elle-même appelle Reception.
Nous placerons le contenu suivant dans le fichier JS_Exemple_3_1.js, à la suite du contenu existant:
//--------------------------------
// Fichier: JS_Exemple_3_1_.js
// Methode Reception
//--------------------------------
function Reception ()
{
// SI ( L'état de l'objet XHR est "complète") ALORS Insérer le code HTML reçu dans le cadre "Affichage"
if( this.XHR.readyState == 4 )
{
document.getElementById ( "Affichage" ).innerHTML = this.XHR.responseText;
// FINSI
}
}
//-------------------------------------------------------------------------------------------------
// Cette fonction, connectée directement au gestionnaire d'événement, sert de relais pour l'appel
// de la méthode Reception. En effet, l'appel direct d'une méthode par un gestionnaire d'événement
// ne semble pas fonctionner !!!
//-------------------------------------------------------------------------------------------------
function Rec()
{
ObjGestionAjax.Reception();
}
III.4.7.RESULTATS:
Voici l'affichage donné par l'exemple (avant le clic sur le cadre):
Pour afficher, cliquer dans le cadre ci-dessous:
En cliquant dans le cadre, on déclenche la transaction client-serveur AJAX et l'affichage de la liste contenue dans le
fichier "Contenu.html" dans ce cadre. On remarquera que l'échange s'effectue sans que le navigateur signale quoi que ce soit, et
qu'il est beaucoup plus rapide qu'un appel de page classique, puisque les données échangées se réduisent ici à cette liste
et non à la page complète.